#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :chapter05.py
# @Time      :2021/03/31 15:36:16
# @Author    :Raink


from typing import List, Optional
from fastapi import APIRouter, Depends

app05 = APIRouter()

"""
依赖注入：指在编程中，为保证代码成功运行，先导入或声明其所
需的“依赖”，如子函数、数据库连接等

优点：
1、提高代码的复用率
2、共享数据库的连接
3、增强安全、认证和角色管理
"""

async def common_parameters(q:Optional[str]=None, page:int=1, limit:int=100):
    return {"q": q, "page":page, "limit":limit}
"""
上面的公共函数在在面的两个方法中都要依赖
dependency01，dependency02要传入的值，就是common_parameters返回的值
因此就需要注入这个依赖
需要使用fastapi中的Depends

不区分异步函数，异步函数可以依赖非异步的函数，非异步函数也可以依赖异步函数
"""
@app05.get("/dependency01")
async def dependency01(commons: dict = Depends(common_parameters)):
    return commons
@app05.get("/dependency02")
def dependency02(commons: dict = Depends(common_parameters)):
    return commons

"""
类作为依赖
接口参数是类的初始化函数
"""
fake_items_db = [{"item_name":"Foo"}, {"item_name":"Bar"}, {"item_name":"Baz"}]
class CommonQueryParams:
    def __init__(self, q: Optional[str]=None, page:int=1, limit:int=100):
        self.q = q
        self.page = page
        self.limit = limit

# 三种不同写法
@app05.get("/classes_as_dependencies")
# async def classes_as_dependencies(commoms: CommonQueryParams=Depends(CommonQueryParams))
# async def classes_as_dependencies(commons: CommonQueryParams=Depends())
async def classes_as_dependencies(commons=Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.page:commons.page+commons.limit]
    response.update({"items":items})
    return response


"""
嵌套依赖，子依赖
"""

def query(q: Optional[str] = None):
    pass
    return q

def sub_query(last_query: str, q: str = Depends(query)):
    if not q:
        return last_query
    return {"query": q, "sub_query": last_query}

@app05.get("/sub_dependency")
async def sub_dependency(final_query: str = Depends(sub_query, use_cache=True)):
    """
    use_cache默认值是True,表示当多个依赖有一个共同子依赖时，
    每次request请求只会调用子依赖一次，
    多次调用将从缓存中获取
    """
    return {"sub_dependency": final_query}


"""
在路径操作中导入依赖
"""

from fastapi import Header, HTTPException

async def verify_token(x_token:str = Header(...)):
    # 没有返回值的子依赖
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X_Token header invalid")
    return x_token

async def verify_key(x_key:str = Header(...)):
    # 有返回值的子依赖,但返回值不会被调用
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X_Key header invalid")
    return x_key

# 不在参数中依赖，而在路劲装饰器中依赖
@app05.get("/dependency_in_path_operation", dependencies=[Depends(verify_token), Depends(verify_key)])
async def dependency_in_path_operation():
    return [{"user":"user01"},{"user":"user02"}]


"""
全局依赖
直接在APIRouter和FastAPI中直接依赖
"""
# app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
# app05 = APIRouter(dependencies=[Depends(verify_token), Depends(verify_key)])

"""
带yield关键字的依赖
python3.7+才支持，python3.6需要pip install async-exit-stack async-generator
用伪代码来展示
"""
# 数据库的连接（伪代码）
async def get_db():
    db = "de_connection"
    try:
        yield db
    finally:
        db.endswith("db_closs")

async def dependency_a():
    dep_a = "generator_dep_a"
    try: 
        yield dep_a
    finally:
        dep_a.endswith("db_close")

async def dependency_b(dep_a = Depends(dependency_a)):
    dep_b = "generator_dep_b"
    try: 
        yield dep_b
    finally:
        dep_b.endswith(dep_a)

async def dependency_c(dep_b = Depends(dependency_b)):
    dep_c = "generator_dep_c"
    try: 
        yield dep_c
    finally:
        dep_c.endswith(dep_b)